﻿using System;
using System.Collections.Generic;
using System.Linq;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;

namespace IQIGame.Onigao.Logic
{
    public class BattleMapController : TController
    {
        private BattlePlayController mPlayCtrl;

        /// <summary>
        /// 精度 格子宽度 (6变形边长
        /// </summary>
        private static LFloat cellWidth = 0.2f.ToLFloat();
        private static LFloat Sqrt3 = LMath.Sqrt(3.ToLFloat());

        public int mapWidth;

        public LVector2 startPos { get; private set; }
        private int width;
        private int height;
        public LFloat radius { get; private set; }

        private BattleMapNode[,] nodes;

        private List<BattleMapNode> openList = new List<BattleMapNode>();
        private List<BattleMapNode> closedList = new List<BattleMapNode>();

#if UNITY_EDITOR
        public BattleMapNode[,] Map => this.nodes;
#endif


        public void Initialize(BattlePlayController playCtrl, int nBattleSceneID)
        {
            var rConfig = TableCenter.battleScene.Get(nBattleSceneID);
            this.radius = (rConfig.BattleRadius / 1000f).ToLFloat();
            var fSqrRadius = this.radius * this.radius;
            this.mapWidth = (this.radius.ToInt() + 1) * 2;
            this.mPlayCtrl = playCtrl;
            this.startPos = new LVector2(-(this.mapWidth / 2f).ToLFloat(), -(this.mapWidth / 2f).ToLFloat());

            this.width = (int)(this.mapWidth / (3 * cellWidth) + 1).Ceil();
            this.height = (int)(this.mapWidth / (Sqrt3 * cellWidth / 2) + 1).Ceil();
            this.nodes = new BattleMapNode[this.width, this.height];
            for (int i = 0; i < this.width; i++)
            {
                for (int j = 0; j < this.height; j++)
                {
                    this.nodes[i, j] = new BattleMapNode(i, j, this.MapToWorldPos(new LVector2Int(i, j)));
                    var rWorldPos = this.MapToWorldPos(this.nodes[i, j].pos);
                    if (rWorldPos.sqrMagnitude > fSqrRadius)
                    {
                        this.SetMapNodeObstruct(this.nodes[i, j], EObstructType.OutSide, true, 0);
                    }
                }
            }
        }

        /// <summary>
        /// 根据世界坐标对地图设置障碍，并返回被修改了的点列表
        /// </summary>
        /// <param name="worldPos"></param>
        /// <param name="worldRadius"></param>
        /// <param name="list">返回被修改了的点列表</param>
        /// <param name="obstructUnitID"></param>
        public void SetObstruct(LVector2 worldPos, LFloat worldRadius, ref List<LVector2Int> list, int obstructUnitID)
        {
            var rSelfCoor = this.WorldToMapPos(worldPos);
            var nCoorRadius = this.WorldToMapLength(worldRadius);
            var fSqrRadius = worldRadius * worldRadius;
            var rList = ListPool<LVector2Int>.Get();
            this.GetNeighborCoor(rSelfCoor, ref rList, nCoorRadius);
            // 如果没有就加一下自己
            if (!rList.Contains(rSelfCoor))
            {
                rList.Add(rSelfCoor);
            }
            for (int i = 0; i < rList.Count; i++)
            {
                var rPos = rList[i];
                if (list.Contains(rPos))
                {
                    continue;
                }

                var rDis = (this.MapToWorldPos(rPos) - worldPos).sqrMagnitude;
                if (rDis <= fSqrRadius)
                {
                    list.Add(rPos);
                    this.SetMapNodeObstruct(rPos, EObstructType.Role, true, obstructUnitID);
                }
            }
            ListPool<LVector2Int>.Put(rList);
        }

        public void SetMapNodeObstruct(LVector2Int pos, EObstructType obType, bool obstruct, int obstructUnitID)
        {
            var nX = pos.x;
            var nY = pos.y;
            if (nX >= 0 && nX < this.width && nY >= 0 && nY < this.height)
            {
                var rNode = this.nodes[nX, nY];
                this.SetMapNodeObstruct(rNode, obType, obstruct, obstructUnitID);
            }
        }

        private void SetMapNodeObstruct(BattleMapNode node, EObstructType obType, bool obstruct, int obstructUnitID)
        {
            var rOld = node.obstructType;
            if (obstruct)
            {
                node.obstructType = rOld | obType;
                node.obstructUnitID = obstructUnitID;
            }
            else
            {
                obType = ~obType;
                node.obstructType = rOld & obType;
                node.obstructUnitID = 0;
            }
        }

        // A*寻路算法
        public bool FindPath(LVector2 worldStart, LVector2 worldEnd, LFloat worldRadius, int obstructID, ref List<LVector2Int> path, LFloat maxStartDistance, LFloat maxEndDistance)
        {
            var start = this.WorldToMapPos(worldStart);
            var end = this.WorldToMapPos(worldEnd);
            var maxStartDisSqrt = maxStartDistance * maxStartDistance;
            var maxEndDisSqrt = maxEndDistance * maxEndDistance;
            var coorRadius = this.WorldToMapLength(worldRadius);
            // 初始化开放列表和关闭列表
            this.openList.Clear();
            this.closedList.Clear();

            var rStartNode = this.GetNode(start.x, start.y);
            rStartNode.disEndSqrt = (worldStart - worldEnd).sqrMagnitude;
            var rEndNode = this.GetNode(end.x, end.y);
            rEndNode.disStartSqrt = (worldStart - worldEnd).sqrMagnitude;

            // 将起点加入开放列表
            this.openList.Add(rStartNode);
            BattleMapNode rNearestBattleMapNode = null;
            while (openList.Count > 0)
            {
                // 从开放列表中选择f值最小的节点作为当前节点
                BattleMapNode current = this.openList[0];
                for (int i = 1; i < this.openList.Count; i++)
                {
                    if (this.openList[i].f < current.f)
                    {
                        current = this.openList[i];
                    }
                }

                if (current.h > 0 && (rNearestBattleMapNode == null || current.h < rNearestBattleMapNode.h))
                {
                    rNearestBattleMapNode = current;
                }

                // 将当前节点从开放列表中移除，加入关闭列表
                this.openList.Remove(current);
                this.closedList.Add(current);

                // 判断是否到达目标节点
                if (current.disEndSqrt <= maxEndDisSqrt)
                {
                    this.GeneratePath(current, ref path);
                    return true;
                }

                // 获取当前节点周围可到达的节点
                var rNeighbors = ListPool<BattleMapNode>.Get();
                this.GetNeighbors(current, coorRadius, obstructID, ref rNeighbors);
                foreach (BattleMapNode neighbor in rNeighbors)
                {
                    // 如果邻居节点已经在关闭列表中，则忽略
                    if (this.closedList.Contains(neighbor))
                    {
                        continue;
                    }

                    int g = current.g + 1; // 假设每个相邻节点的移动成本为1

                    // 如果邻居节点不在开放列表中，则加入开放列表
                    if (!this.openList.Contains(neighbor))
                    {
                        neighbor.disEndSqrt = (neighbor.worldPos - worldEnd).sqrMagnitude;
                        neighbor.disStartSqrt = (neighbor.worldPos - worldStart).sqrMagnitude;
                        if (neighbor.disStartSqrt <= maxStartDisSqrt)
                        {
                            neighbor.g = g;
                            neighbor.h = GetHeuristic(neighbor, rEndNode);
                            neighbor.f = neighbor.g + neighbor.h;
                            neighbor.parent = current;
                            this.openList.Add(neighbor);
                        }
                        else
                        {
                            this.closedList.Add(neighbor);
                        }
                    }
                    else
                    {
                        // 如果邻居节点已经在开放列表中，判断是否通过当前节点到达邻居节点的移动成本更小
                        if (g < neighbor.g)
                        {
                            neighbor.g = g;
                            neighbor.f = neighbor.g + neighbor.h;
                            neighbor.parent = current;
                        }
                    }
                }
                ListPool<BattleMapNode>.Put(rNeighbors);
            }

            // 如果开放列表为空，表示无法到达目标节点
            this.GeneratePath(rNearestBattleMapNode, ref path);
            return false;
        }

        public bool GetMapCanUse(LVector2 worldPos, LFloat worldRadius, out LVector2 pos)
        {
            var rSelfCoor = this.WorldToMapPos(worldPos);
            var nCoorRadius = this.WorldToMapLength(worldRadius);

            bool bSelected = false;
            pos = worldPos;
            var rOpenList = ListPool<LVector2Int>.Get();
            var rCloseList = ListPool<LVector2Int>.Get();
            var rCoor = rSelfCoor;
            var nStack = 0;
            while (nStack++ < 3) // 3层
            {
                if (this.MapCanUse(0, rCoor, nCoorRadius))
                {
                    pos = this.MapToWorldPos(rCoor);
                    bSelected = true;
                    break;
                }
                if (rOpenList.Count <= 0)
                {
                    nStack++;
                    var rList = ListPool<LVector2Int>.Get();
                    this.GetNeighborCoor(rSelfCoor, ref rList, nStack);
                    for (int i = 0; i < rList.Count; i++)
                    {
                        if (!rCloseList.Contains(rList[i]))
                        {
                            rOpenList.Add(rList[i]);
                        }
                    }
                }
                if (rOpenList.Count > 0)
                {
                    rCloseList.Add(rCoor);
                    rCoor = rOpenList[^1];
                    rOpenList.RemoveAt(rOpenList.Count - 1);
                }
            }

            return bSelected;
        }

        public bool MapCanUse(int obstructID, LVector2 worldPos, LFloat worldRadius)
        {
            var rSelfCoor = this.WorldToMapPos(worldPos);
            var nCoorRadius = this.WorldToMapLength(worldRadius);
            return this.MapCanUse(obstructID, rSelfCoor, nCoorRadius);
        }

        private bool MapCanUse(int obstructID, LVector2Int mapPos, int mapRadius)
        {
            if (!this.GetMap(obstructID, mapPos.x, mapPos.y))
            {
                return false;
            }
            var rNeighborList = ListPool<LVector2Int>.Get();
            this.GetNeighborCoor(mapPos, ref rNeighborList, mapRadius);
            var fSqrRadius = mapRadius * mapRadius;
            for (int i = 0; i < rNeighborList.Count; i++)
            {
                var rPos = rNeighborList[i];
                if (!this.GetMap(obstructID, rPos.x, rPos.y))
                {
                    var rDis = (rPos - mapPos).sqrMagnitude;
                    if (rDis <= fSqrRadius)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        private bool GetMap(int obstructID, int x, int y)
        {
            var rNode = this.nodes[x, y];
            return (obstructID != 0 && rNode.obstructUnitID == obstructID) || rNode.obstructType == EObstructType.None;
        }

        // 定义奇偶行的偏移数组
        static readonly LVector2Int[] oddRowOffset = new LVector2Int[]
        {
            new LVector2Int(0, 2), // 偏移 (0, +2)
            new LVector2Int(-1, 1), // 偏移 (-1, +1)
            new LVector2Int(-1, -1), // 偏移 (-1, -1)
            new LVector2Int(0, -2), // 偏移 (0, -2)
            new LVector2Int(0, 1), // 偏移 (0, +1)
            new LVector2Int(0, -1) // 偏移 (0, -1)
        };

        static readonly LVector2Int[] evenRowOffset = new LVector2Int[]
        {
            new LVector2Int(0, 2), // 偏移 (0, +2)
            new LVector2Int(0, 1), // 偏移 (0, +1)
            new LVector2Int(0, -1), // 偏移 (0, -1)
            new LVector2Int(0, -2), // 偏移 (0, -2)
            new LVector2Int(1, 1), // 偏移 (1, +1)
            new LVector2Int(1, -1) // 偏移 (1, -1)
        };

        private void GetNeighborCoor(LVector2Int center, ref List<LVector2Int> list, int range)
        {
            if (range == 1)
            {
                this.GetNeighborCoor(center, ref list);
            }
            else if (range > 1)
            {
                range--;
                var rList = ListPool<LVector2Int>.Get();
                this.GetNeighborCoor(center, ref rList);
                for (int i = 0; i < rList.Count; i++)
                {
                    this.GetNeighborCoor(center, ref list, range);
                }
                ListPool<LVector2Int>.Put(rList);
            }
        }

        private void GetNeighborCoor(LVector2Int center, ref List<LVector2Int> list)
        {
            bool bCheckRepeat = list.Count > 0; // list原来有内容就需要查重
            var offset = (center.y % 2 == 0) ? oddRowOffset : evenRowOffset;
            for (int i = 0; i < offset.Length; i++)
            {
                var rValue = center + offset[i];
                if (bCheckRepeat && list.Contains(rValue))
                {
                    continue;
                }
                list.Add(rValue);
            }
        }

        private void GetNeighbors(BattleMapNode battleMapNode, int coorRadius, int obstructID, ref List<BattleMapNode> neighbors)
        {
            var offset = ListPool<LVector2Int>.Get();
            this.GetNeighborCoor(battleMapNode.pos, ref offset);
            for (int i = 0; i < offset.Count; i++)
            {
                int nx = offset[i].x;
                int ny = offset[i].y;
                var rNode = this.GetNode(nx, ny);
                if (rNode != null && MapCanUse(obstructID, new LVector2Int(nx, ny), coorRadius))
                {
                    neighbors.Add(rNode);
                }
            }
            ListPool<LVector2Int>.Put(offset);
        }

        private BattleMapNode GetNode(int nX, int nY)
        {
            if (nX < 0)
            {
                nX = 0;
            }
            else if (nX >= this.width)
            {
                nX = this.width - 1;
            }
            if (nY < 0)
            {
                nY = 0;
            }
            else if (nY >= this.height)
            {
                nY = this.height - 1;
            }
            return this.nodes[nX, nY];
        }

        // 生成最终路径
        private void GeneratePath(BattleMapNode battleMapNode, ref List<LVector2Int> path)
        {
            BattleMapNode current = battleMapNode;

            while (current != null)
            {
                path.Add(new LVector2Int(current.x, current.y));
                current = current.parent;
            }

            path.Reverse(); // 反转路径，使起点位于列表的开头

            foreach (var rNode in this.nodes)
            {
                rNode.Reset();
            }
        }

        // 计算启发式函数（估算从当前节点到目标节点的成本）
        private int GetHeuristic(BattleMapNode battleMapNode, BattleMapNode target)
        {
            // 使用曼哈顿距离作为启发式函数
            int dx = Math.Abs((battleMapNode.worldPos.x - target.worldPos.x).ToInt());
            int dy = Math.Abs((battleMapNode.worldPos.y - target.worldPos.y).ToInt());
            return dx + dy;
        }

        protected override void OnDispose()
        {
            this.nodes = null;
            this.width = 0;
            this.height = 0;
        }

        private LVector2Int[] mapNear3 = new LVector2Int[3];

        public LVector2Int WorldToMapPos(LVector2 worldPos)
        {
            // 根据坐标获取到最近的三个六边形，计算出谁距离最近，自己就落在哪个六边形中
            var rPos = worldPos - this.startPos;
            var nY1 = (int)(rPos.y / cellWidth / Sqrt3 * 2).Floor();
            var nX1 = (int)(rPos.x / cellWidth / 3.ToLFloat()).Floor();
            var nX2 = nX1 + 1;
            var nY2 = nY1 + 1;
            this.mapNear3[0] = new LVector2Int(nX1, nY1);
            this.mapNear3[1] = new LVector2Int(nX2, nY1);
            this.mapNear3[2] = new LVector2Int(nX1, nY2);
            var fMinDisSqr = -LFloat.one;
            var nIndex = 0;
            for (int i = 0; i < 3; i++)
            {
                var fDisSqr = (this.MapToWorldPos(this.mapNear3[i]) - worldPos).sqrMagnitude;
                if (fMinDisSqr < LFloat.zero || fDisSqr < fMinDisSqr)
                {
                    fMinDisSqr = fDisSqr;
                    nIndex = i;
                }
            }
            return this.mapNear3[nIndex];
        }

        public LVector2 MapToWorldPos(LVector2Int mapPos)
        {
            return (mapPos.y % 2) switch
            {
                (0) => new LVector2(
                           mapPos.x.ToLFloat() * 3 * cellWidth,
                           mapPos.y.ToLFloat() * Sqrt3 * cellWidth * 0.5f.ToLFloat())
                       + this.startPos,
                (1) => new LVector2(
                           (mapPos.x.ToLFloat() * 3 + 1.5f.ToLFloat()) * cellWidth,
                           mapPos.y.ToLFloat() * Sqrt3 * cellWidth * 0.5f.ToLFloat())
                       + this.startPos,
                _ => throw new Exception()
            };
        }

        private int WorldToMapLength(LFloat worldLength)
        {
            return (int)(worldLength / cellWidth / Sqrt3).Ceil();
        }

        [Flags]
        public enum EObstructType
        {
            None,
            Role = 1,
            OutSide = 1 << 1,
        }

        public class BattleMapNode
        {
            // 坐标
            public int x => this.pos.x;
            public int y => this.pos.y;
            public LVector2Int pos { get; private set; }
            public LVector2 worldPos { get; set; }

            public int g; // 从起点到当前节点的移动成本
            public int h; // 从当前节点到目标节点的预估成本
            public int f; // f = g + h

            public LFloat disStartSqrt; // 当前点到起点的直线距离平方
            public LFloat disEndSqrt; // 当前点到终点的直线距离平方
            public BattleMapNode parent;

            public EObstructType obstructType;
            public int obstructUnitID;

            public void Reset()
            {
                this.g = 0;
                this.h = 0;
                this.f = 0;
                this.disEndSqrt = 0;
                this.disStartSqrt = 0;
                this.parent = null;
            }

            public BattleMapNode(int x, int y, LVector2 worldPos)
            {
                this.pos = new LVector2Int(x, y);
                this.worldPos = worldPos;
            }
        }
    }
}